const TcbRouter = require('tcb-router')
const cloud = require('wx-server-sdk')
const { WXMINIUser, WXPay, WXPayConstants, WXPayUtil } = require('wx-js-utils')
const axios = require('axios')
const dayjs = require('dayjs')

cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })

const db = cloud.database()
const _ = db.command

exports.main = async event => {
  const { APPID: appId, OPENID: _openid } = cloud.getWXContext()
  if (!_openid) {
    throw new Error('no openid')
  }

  const {
    data: [uData],
  } = await db.collection('user').where({ _openid }).get()
  if (!uData) {
    throw new Error('no login user')
  }
  const user = {
    _id: uData._id,
    nickName: uData.nickName,
    avatarUrl: uData.avatarUrl,
    gender: uData.gender,
    phoneNumber: uData.phoneNumber,
  }

  const { mchId, key } = {
    mchId: '1601711849',
    key: 'HMkl734lO6B8wQ509nvseQPZ47SlL4vz',
  }
  const pay = new WXPay({
    appId,
    mchId,
    key,
    timeout: 10000,
    signType: WXPayConstants.SIGN_TYPE_MD5,
    useSandbox: false,
  })

  const {
    data: [{ appId: appIdB, secret, env }],
  } = await db.collection('config').get()
  const wXMINIUser = new WXMINIUser({ appId: appIdB, secret })
  const token = await wXMINIUser.getCacheAccessToken()

  const createAt = new Date()
  const currDate = dayjs(createAt).format('YYYY-MM-DD')

  const app = new TcbRouter({ event })

  app.router(['add-order'], async ctx => {
    const { promotionCastId } = event
    if (!promotionCastId) {
      throw new Error('no promotion-cast id')
    }

    let {
      data: { errcode, errmsg, resp_data: pData },
    } = await axios.post(
      'https://api.weixin.qq.com/tcb/invokecloudfunction',
      { _id: promotionCastId },
      { params: { access_token: token, env, name: 'list-all-promotion-cast' } }
    )
    if (errcode) {
      throw new Error(errmsg)
    }
    ;[pData] = JSON.parse(pData)
    if (!pData) {
      throw new Error('no promotion-cast')
    }

    const { promotion, currQuota } = pData
    if (!currQuota[currDate] || currQuota[currDate] <= 0) {
      throw new Error('exceed quota')
    }

    const cost = promotion.price
    const data = {
      _openid,
      promotionCast: pData,
      cost,
      status: 0,
      user,
      createAt,
    }
    const { _id: orderId } = await db.collection('order').add({ data })

    const fee = cost * 100
    //const fee = 1
    const result = await pay.unifiedOrder({
      body: promotion.name,
      out_trade_no: orderId,
      spbill_create_ip: '127.0.0.1',
      notify_url: 'http://www.qq.com',
      total_fee: fee,
      trade_type: 'JSAPI',
      openid: _openid,
    })
    if (result.return_code !== 'SUCCESS' || result.result_code !== 'SUCCESS') {
      throw new Error('unified order failed')
    }

    const nonceStr = result.nonce_str
    const package = `prepay_id=${result.prepay_id}`
    const signType = WXPayConstants.SIGN_TYPE_MD5
    const timeStamp = '' + Math.ceil(createAt.getTime() / 1000)
    const paySign = WXPayUtil.generateSignature(
      { appId, nonceStr, package, signType, timeStamp },
      key
    )
    const payment = { nonceStr, package, signType, timeStamp, paySign }

    await db.collection('order').doc(orderId).update({ data: { payment } })

    ctx.body = { orderId, payment }
  })

  app.router(['update-order'], async ctx => {
    const { _id } = event
    if (!_id) {
      throw new Error('no orderId')
    }

    const {
      data: [oData],
    } = await db.collection('order').where({ _openid, _id, status: 0 }).get()
    if (!oData) {
      throw new Error('order not found')
    }

    const result = await pay.orderQuery({ out_trade_no: _id })
    const success =
      result.return_code === 'SUCCESS' &&
      result.result_code === 'SUCCESS' &&
      result.trade_state === 'SUCCESS'
    await db
      .collection('order')
      .doc(_id)
      .update({
        data: {
          result,
          status: success ? 1 : undefined,
          checkin: {
            createBefore: dayjs(createAt).add(7, 'day').toDate(),
          },
        },
      })
    if (!success) {
      throw new Error('trade failed')
    }

    await axios.post(
      'https://api.weixin.qq.com/tcb/databaseupdate',
      {
        env,
        query: `
          db.collection('promotion-cast')
            .doc('${oData.promotionCast._id}')
            .update({
              data: {
                currQuota: {
                  '${currDate}': db.command.inc(-1)
                }
              },
            })
        `,
      },
      { params: { access_token: token } }
    )

    const cStats = {
      orderCount: db.command.inc(1),
      cost: db.command.inc(oData.cost),
      costSaving: db.command.inc(
        oData.promotionCast.promotion.originalPrice - oData.cost
      ),
    }

    await db
      .collection('user')
      .where({ _openid })
      .update({ data: { stats: cStats } })
  })

  return app.serve()
}
